package com.cmcc.volte.xdr.SigXDR;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.cmcc.volte.xdr.XDRUtil.XDRUtil;

/**
 * SCTP分片重组，注:需要先做完IP分片重组
 * @Date 2017年8月2日 下午9:46:48
 * @author lvpin
 *
 */
public class SigXDRSctpReassemble {
	private static Logger logger = LogManager.getLogger(SigXDRSctpReassemble.class);
	
	public static SigXDR reassemble(SigXDR sigXDR){
		
		ArrayList<ReassembleAgent> reassembleAgentList = new ArrayList<ReassembleAgent>();
		
		List<SigXDRData> sigXDRDataList = sigXDR.getData();
		//int i=1;//only for systemout test
		for(SigXDRData sigXDRData : sigXDRDataList){

			byte[] loadBytes = sigXDRData.getLoad();
			
			//处理8100即VirtualLan情况的数据包,会多4个字节的VirtualLan头域,需要向后移动4位才能获得实际数据
			int moveSize = 0;
			byte[] tmp = new byte[2];
			tmp[0] = loadBytes[12];
			tmp[1] = loadBytes[13];
			if("8100".equals(XDRUtil.bytesToHexString(tmp))){
				moveSize = 4;
			}
			
			byte ipByte10 = loadBytes[23 + moveSize];		
			int protocol = bytesToInt(new byte[]{ipByte10});//该头占1个字节，指明传输层协议。IANA统一分配，UDP：0x11(17)、TCP：0x06(6)、ICMP：0x01(1)、SCTP：0x84(132)
			
			if(protocol != 0x84)//目前只考虑处理SCTP分片重组，TCP需要吗？TOFIX
				return sigXDR;//此处如果不是SCTP包，直接返回
			
			byte sctpByte13 = loadBytes[46 + moveSize];
			int sctpFirstChunkType = sctpByte13;//TODO TOFIX 紧跟在SCTP Common Header后面的如果不是Data Chunk 分包如何重组...，目前已知的有SACK Chunk 占16字节 
			if( sctpFirstChunkType != 0 ){//0x00  Data Chunk   0x03 SACK Chunk
				logger.info("This is a SCTP incloud SACK Chunk");
				moveSize = moveSize + 16;
			}
			
			byte sctpByte14 = loadBytes[47 + moveSize];
			int flags = sctpByte14;
			
			byte[] sctpByte23_24 = new byte[2];
			sctpByte23_24 = Arrays.copyOfRange(loadBytes, 56+moveSize, 58+moveSize);
			int streamSequenceNumber = bytesToInt(sctpByte23_24);
			
			SigXDRSctpReassemble sigXDRSctpReassemble = new SigXDRSctpReassemble();
			SigXDRSctpReassemble.SigXDRDataAgent sigXDRDataAgent = sigXDRSctpReassemble.new SigXDRDataAgent(flags,streamSequenceNumber,sigXDRData,moveSize);
			
			
//			System.out.println("-------------------"+i++);
//			System.out.println("Len : "+sigXDRData.getLen());
//			System.out.println("Time_s : "+sigXDRData.getTime_s());
//			System.out.println("Time_ns : "+sigXDRData.getTime_ns());
//			System.out.println("Load.length : "+sigXDRData.getLoad().length);
//			System.out.println("--");			
//			System.out.println("protocol : "+protocol);
//			System.out.println("sctpFirstChunkType : "+sctpFirstChunkType);
//			System.out.println("flags : "+flags);
//			System.out.println("streamSequenceNumber : "+streamSequenceNumber);
//			byte[] sctpByte15_16 = new byte[2];
//			sctpByte15_16 = Arrays.copyOfRange(loadBytes, 48+moveSize, 50+moveSize);
//			int chunkLength = bytesToInt(sctpByte15_16);
//			System.out.println("chunkLength : "+chunkLength);
			
			storeSigXDRDataAgent_3(reassembleAgentList,sigXDRDataAgent,flags,streamSequenceNumber);
		}
		
		if(reassembleAgentList.size() == sigXDR.getHeader().getSize()) //没有分片直接返回
			return sigXDR;    
		
//		System.out.println("---------reassembleAgentList.size()----------"+reassembleAgentList.size());
		
		//-------重新拼装SigXDR  START--------
		SigXDR newSigXDR = new SigXDR();
		
		//拼装SigXDRHeader
		SigXDRHeader sigXDRHeader = new SigXDRHeader();
		sigXDRHeader.setSize((short)reassembleAgentList.size());//设置重新拼装后的SigXDR的Size
		sigXDRHeader.setInterface(sigXDR.getHeader().getInterface());
		newSigXDR.setHeader(sigXDRHeader);
		
		//拼装Data  即List<SigXDRData>
		List<SigXDRData> newSigXDRDataList = new ArrayList<SigXDRData>();
		//int j =1;//only for systemout
		for(ReassembleAgent reassembleAgent : reassembleAgentList){
			SigXDRData newSigXDRData = reassembleAgent.reassemble();//reassembleAgent.reassemble()是真正执行IP分片重新组装的方法
			newSigXDRDataList.add(newSigXDRData);
						
//			System.out.println("-------------------"+j++);			
//			System.out.println("Len : "+newSigXDRData.getLen());
//			System.out.println("Time_s : "+newSigXDRData.getTime_s());
//			System.out.println("Time_ns : "+newSigXDRData.getTime_ns());
//			System.out.println("Load.length : "+newSigXDRData.getLoad().length);
		}
		newSigXDR.setData(newSigXDRDataList);
		//-------重新拼装SigXDR  STOP--------
			
		return newSigXDR;
	}


	/**
	 * 将属于同一个SCTP的分片的包保存到一个List里
	 * 
	 * 
	 * 
	 * @param reassembleAgentList
	 * @param sigXDRDataAgent
	 * @param flags
	 * @param streamSequenceNumber
	 */
	public static void storeSigXDRDataAgent_3(ArrayList<ReassembleAgent> reassembleAgentList,SigXDRDataAgent sigXDRDataAgent,int flags,int streamSequenceNumber){
		//int MF = flags & 0x0001;
		SigXDRSctpReassemble sigXDRSctpReassemble = new SigXDRSctpReassemble();
		if(flags == 0x03 || flags == 0x02){// 0x03(非分分片SCTP):First Segment&Last Segment , 0x02(SCTP第一个分片):First Segment&Not the Last Segement .处理方式一样.
			ReassembleAgent reassembleAgent = sigXDRSctpReassemble.new ReassembleAgent();
			reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
			reassembleAgent.setStreamSequenceNumber(sigXDRDataAgent.getStreamSequenceNumber());			
			reassembleAgentList.add(reassembleAgent);
		}else{//0x00(SCTP中间分片):Subsequent Segment & Not the Last Segement  ,  0x01(SCTP最后一个分片):Subsequent Segment &  Last Segment 
			for(ReassembleAgent reassembleAgent : reassembleAgentList){
				if(reassembleAgent.getStreamSequenceNumber() == sigXDRDataAgent.getStreamSequenceNumber()){
					reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
					return;
				}				
			}
			System.out.println("SCTP Reassembel Something Wrong");
		}
	}
	
	public class ReassembleAgent{
		
		int streamSequenceNumber;
		ArrayList<SigXDRDataAgent> sigXDRDataAgentList = new ArrayList<SigXDRDataAgent>();
		
		public int getStreamSequenceNumber() {
			return streamSequenceNumber;
		}
		public void setStreamSequenceNumber(int streamSequenceNumber) {
			this.streamSequenceNumber = streamSequenceNumber;
		}
		public ArrayList<SigXDRDataAgent> getSigXDRDataAgentList() {
			return sigXDRDataAgentList;
		}
		public void setSigXDRDataAgentList(
				ArrayList<SigXDRDataAgent> sigXDRDataAgentList) {
			this.sigXDRDataAgentList = sigXDRDataAgentList;
		}

		public SigXDRData reassemble(){
//			System.out.println("---------sigXDRDataAgentList.size()------------"+sigXDRDataAgentList.size());

			//TODO 此处需要对sigXDRDataAgentList中的包按照offset排个序？？
				
			SigXDRData newSigXDRData = new SigXDRData();
			SigXDRData firstSigXDRData = sigXDRDataAgentList.get(0).getSigXDRData();
			newSigXDRData.setLen(firstSigXDRData.getLen());
			newSigXDRData.setVer(firstSigXDRData.getVer());
			newSigXDRData.setLinkType(firstSigXDRData.getLinkType());
			newSigXDRData.setCardType(firstSigXDRData.getCardType());
			newSigXDRData.setCardId(firstSigXDRData.getCardId());
			newSigXDRData.setTime_s(firstSigXDRData.getTime_s());
			newSigXDRData.setTime_ns(firstSigXDRData.getTime_ns());
			byte[] firstload = firstSigXDRData.getLoad();
			newSigXDRData.setLoad(firstload);//此处SCTP的U、B	、E和Chunk Length为第一个分片的值	
			

			
			for( int i=1 ; i<sigXDRDataAgentList.size() ; i++){	
				int moveSizeSub = sigXDRDataAgentList.get(i).getMoveSize();//SCTP中间分片的moveSize(可能包括4字节的VirtualLan 和  16字节的SACK Chunk)
				byte[] load = newSigXDRData.getLoad();
				byte[] load1 = sigXDRDataAgentList.get(i).getSigXDRData().getLoad();
				byte[] load1Sub = Arrays.copyOfRange(load1, 62+moveSizeSub, load1.length);//reassemble时应该去掉SCTP中间分片包的Enternet(14)、IP(20)、SCTP头域(12)、SCTP DATA Chunk(16) 即 14+20+12+16=62字节,以及moveSzie
				byte[] newLoad = arraycat(load,load1Sub);
//				System.out.println("******load : "+load.length);
//				System.out.println("******load1 : "+load1.length);
//				System.out.println("******load1Sub : "+load1Sub.length);
//				System.out.println("******newLoad : "+newLoad.length);
//				System.out.println("******newLoad-46 : "+(newLoad.length-46));
				
				int moveSize = sigXDRDataAgentList.get(0).getMoveSize();//第一个SCTP分片的moveSize（可能包括4字节的VirtualLan 和  16字节的SACK Chunk）
				int chunkLength = newLoad.length-46-moveSize;//修改SCTP的chunk Length头域，需要在计算长度的时候减去这个moveSize。(SCTP中chunklength值是包括data chunk和diameter长度之和)
				newLoad[49+moveSize] = (byte) (chunkLength & 0x000000FF);
				newLoad[48+moveSize] = (byte) ((chunkLength >> 8) & 0x000000FF);
//				System.out.println("******newLoad[48+moveSize] : "+newLoad[48+moveSize]);
//				System.out.println("******newLoad[49+moveSize] : "+newLoad[49+moveSize]);
				
//				byte[] sctpByte15_16 = new byte[2];
//				sctpByte15_16 = Arrays.copyOfRange(newLoad, 48+moveSize, 50+moveSize);
//				int chunkLengthNew = bytesToInt(sctpByte15_16);
//				System.out.println("******chunkLengthNew : "+chunkLengthNew);				
				newSigXDRData.setLoad(newLoad);
				
				newSigXDRData.setLen((short) (12+newLoad.length));					
			}
			return newSigXDRData;
		}
	}
	
	public class SigXDRDataAgent{
		int flags;
		int streamSequenceNumber;
		SigXDRData sigXDRData;
		int moveSize = 0;
		
		public SigXDRDataAgent(int flags,int streamSequenceNumber,SigXDRData sigXDRData,int moveSize){
			this.flags = flags;
			this.streamSequenceNumber = streamSequenceNumber;
			this.sigXDRData = sigXDRData;
			this.moveSize = moveSize;
		}
		
		public int getFlags() {
			return flags;
		}
		public int getStreamSequenceNumber() {
			return streamSequenceNumber;
		}

		public SigXDRData getSigXDRData() {
			return sigXDRData;
		}
		public int getMoveSize() {
			return moveSize;
		}
		
	}	
	
	/**
	 * convert byte[] to int
	 * @param bytes
	 * @return
	 */
	public static int bytesToInt(byte[] bytes){
		String bytesStr = bytesToStringByBit(bytes);
		Integer bytesInteger = Integer.valueOf(bytesStr,2);
		return bytesInteger;
	}
	
	/**
	 * byte[] convert to String by bit
	 * 
	 * @param bytes
	 * @return
	 */
	public static String bytesToStringByBit(byte[] bytes){
		StringBuffer bytesStr = new StringBuffer();
		for( byte b : bytes ){
			bytesStr.append(byteToStringByBit(b));
		}
		return bytesStr.toString();
	}
	
	/**
	 * byte convert to String by bit
	 * 返回单字节每一位的实际情况
	 * @param b
	 * @return
	 */
	public static String byteToStringByBit(byte b){
		StringBuffer sb = new StringBuffer();
		for (int i = 7; i >= 0; i--) {
			String bitStr = Byte.toString((byte) (b >> i & 1));
            sb.append(bitStr);
        }
		return sb.toString();
	}
	
	/**
	 * 组装两个byte[]
	 * @param buf1
	 * @param buf2
	 * @return
	 */
	public static byte[] arraycat(byte[] buf1,byte[] buf2){
		byte[] bufret=null;
		int len1=0;
		int len2=0;
		if(buf1!=null)
			len1=buf1.length;
		if(buf2!=null)
			len2=buf2.length;
		if(len1+len2>0)
			bufret=new byte[len1+len2];
		if(len1>0)
			System.arraycopy(buf1,0,bufret,0,len1);
		if(len2>0)
			System.arraycopy(buf2,0,bufret,len1,len2);
		return bufret;
	}

}
